package com.zdp.service;

import com.zdp.common.JsonUtils;
import com.zdp.domain.SkillEntity;
import com.zdp.domain.SkillGoods;
import com.zdp.domain.SkillOrder;
import com.zdp.mapper.SkillOrderMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * @author sesshomaru
 * @date 2021/7/25 17:16
 */
@Component
public class MutilThreadOrder {

    public static final String SKILL_GOODS_PHONE = "SKILL_GOODS_PHONE";
    public static final String SKILL_GOODS_LIST = "SKILL_GOODS_LIST";
    // 库存队列
    public static final String SKILL_GOODS_QUEUE = "SKILL_GOODS_QUEUE";

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ProductService productService;
    @Autowired
    private SkillOrderMapper skillOrderMapper;

    // 这就是一个异步的方法会单独的开启一个线程去执行
    @Async
    public void createOrder() throws Exception {
        System.out.println("开始异步抢单");
        SkillEntity skillEntity = (SkillEntity) redisTemplate.boundListOps(SKILL_GOODS_LIST).rightPop();
        if (skillEntity == null) {
            return;
        }
        Long productId = skillEntity.getProductId();
        String userId = skillEntity.getUserId();

        // 高并发情况下这样子可能多个线程都会拿到相同的库存数据，导致超卖
        // 比方说4个线程都拿到了一样的库存，那么最后操作起来实际应该减去4的库存最终更新到缓存中只减去1,这就发生了超卖
        SkillGoods skillGoods = productService.queryByProductId(productId);
        if (skillGoods == null) {
            throw new Exception("商品已经被抢光拉");
        }

        // 能走到这里的就表示库存是大于0的，可以正常下单
        // 这里无需再判断库存是否大于0,因为前面从库存队列中取出时已经判断了
        //if (skillGoods.getStockCount() > 0) {
            // 创建订单
            SkillOrder skillOrder = new SkillOrder();
            skillOrder.setMoney(skillGoods.getCostPrice());
            skillOrder.setPayTime(LocalDateTime.now());
            skillOrder.setStatus("0");
            skillOrder.setUserId(userId);
            skillOrder.setCreateTime(LocalDateTime.now());
            skillOrder.setSkillId(productId);
            skillOrderMapper.insert(skillOrder);
            System.out.println("订单创建成功");

            // 更新缓存中的库存信息
            skillGoods.setStockCount(skillGoods.getStockCount() - 1);
            redisTemplate.boundHashOps(SKILL_GOODS_PHONE).put(String.valueOf(skillGoods.getId()), JsonUtils.objectToJson(skillGoods));
        //}

        // 这个不能使用了, 因为skillGoods本身就有并发安全的问题,应该改为采用判断库存队列的长度来控制
        //if (skillGoods.getStockCount() <= 0) {
        Long stockCount = redisTemplate.boundListOps(SKILL_GOODS_QUEUE + productId).size();
        if (stockCount == null || stockCount <= 0) {
            System.out.println("库存已经是负数了:" + skillGoods.getStockCount());
            redisTemplate.boundHashOps(SKILL_GOODS_PHONE).delete(String.valueOf(skillGoods.getId()));
            skillGoods.setStockCount(Math.toIntExact(stockCount));
            productService.update(skillGoods);
        }
        //}
        System.out.println("结束异步抢单");
    }
}
